package socket.socketByNetty.client;

import com.alibaba.fastjson.JSON;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;
import socket.socketByNetty.utils.message.Message;
import socket.socketByNetty.utils.message.MsgType;

import java.util.Random;

/**
 * Created by think on 2017/10/30.
 */
public class EchoClientHandler extends SimpleChannelInboundHandler<String>{
    //随机产生一个clientId，用于唯一身份注册到服务端
    private  String CLIENTID = new Random().nextInt(1000)+"";

    @Override
    protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
        Message message = JSON.parseObject(msg+"", Message.class);
        MsgType msgType=message.getType();
        switch (msgType){
            case LOGIN:{
                //向服务器发起登录
                message = new Message(MsgType.LOGIN);
                ctx.writeAndFlush(JSON.toJSONString(message));
            }break;
            case PING:{
                System.out.println("messageReceived : receive ping from server----------");
            }break;
            case SEND:{
                //收到服务端消息
                System.out.println("收到服务端消息："+message.getData());
            }break;
            case NO_TARGET:{
                //收到服务端消息
                System.out.println("找不到targetId:"+message.getTargetId());
            }break;
            default:break;
        }
    }

    /**
     * 心跳和重连机制
     * 当客户端的所有ChannelHandler中10s内没有write事件，则会触发userEventTriggered方法，触发心跳
     * 为了减轻服务端对几十万心跳包的回复压力，服务端应该"不回复"心跳，采用服务端读数据的方式来确认客户端是否active（服务端20s读不到数据就是认为客户端挂了）
     * 如果服务端因为某些因素宕机，作为客户端要做的事情就是短线重连
     * 时间设置在IdleStateHandler(readerIdleTimeSeconds,writerIdleTimeSeconds,allIdleTimeSeconds)
     * 该类的第一个参数是指定读操作空闲秒数，第二个参数是指定写操作的空闲秒数，第三个参数是指定读写空闲秒数
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent e = (IdleStateEvent) evt;
            switch (e.state()) {
                case WRITER_IDLE:
                    Message pingMsg=new Message(MsgType.PING);
                    pingMsg.setClientId(CLIENTID);
                    ctx.writeAndFlush(JSON.toJSONString(pingMsg));
                    System.out.println("发送心跳...");
                    break;
                default:
                    break;
            }
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //初始连接时当探针到服务端是活跃的时候，发生送一条消息
        System.out.println(CLIENTID + "新客户端接入!");
        Message loginMsg = new Message(MsgType.LOGIN);
        loginMsg.setClientId(CLIENTID);
        loginMsg.setType(MsgType.LOGIN);
        ctx.writeAndFlush(JSON.toJSONString(loginMsg));
    }

    /**
     * 当链路断开的时候会触发channelInactive这个方法，也就说触发重连的导火索是从这边开始
     * bootstrap.connect(new InetSocketAddress(serverIP, port))
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客户端断开连接,尝试重连机制。。。");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        System.out.println("exceptionCaught 出现异常！");
    }
}
